/*
*  Copyright (c) 2021 Otávio Santana and others
*   All rights reserved. This program and the accompanying materials
*   are made available under the terms of the Eclipse Public License v1.0
*   and Apache License v2.0 which accompanies this distribution.
*   The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
*   and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
*   You may elect to redistribute this code under either of these licenses.
*
*   Contributors:
*
*   Otavio Santana
*/
package {{package}};

import org.eclipse.jnosql.lite.mapping.metadata.LiteEntitiesMetadata;
import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata;
import org.eclipse.jnosql.mapping.metadata.EntityMetadata;
import org.eclipse.jnosql.mapping.metadata.FieldMetadata;

import jakarta.data.Order;
import jakarta.data.page.Page;
import jakarta.data.page.PageRequest;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Default;
import jakarta.inject.Inject;
import org.eclipse.jnosql.mapping.keyvalue.KeyValueTemplate;
import org.eclipse.jnosql.mapping.Database;
import org.eclipse.jnosql.mapping.DatabaseType;

import javax.annotation.processing.Generated;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;


import static java.util.Objects.requireNonNull;
import static org.eclipse.jnosql.mapping.IdNotFoundException.KEY_NOT_FOUND_EXCEPTION_SUPPLIER;

@ApplicationScoped
@Default
@Database(value = DatabaseType.KEY_VALUE)
@Generated(value = "JNoSQL Lite KeyValue Repository Generator", date = "{{now}}")
public class {{className}} implements {{repository}} {

    private final KeyValueTemplate template;

    private final EntityMetadata metadata;

    @Inject
    public {{className}}(KeyValueTemplate template) {
        this.template = Objects.requireNonNull(template, "template is required");
        EntitiesMetadata entities = LiteEntitiesMetadata.INSTANCE;
        this.metadata = entities.get({{entityType}}.class);
    }

    public {{className}}() {
        this.template = null;
        this.metadata = null;
    }

    EntityMetadata entityMetadata(){
        return metadata;
    }

    @Override
    public <S extends {{entityType}}> S save(S entity) {
        requireNonNull(entity, "entity is required");
        this.template.put(entity);
        return entity;
    }

    @Override
    public <S extends {{entityType}}> List<S> saveAll(List<S> entities) {
        requireNonNull(entities, "entities is required");
        return java.util.stream.StreamSupport.stream(this.template.insert(entities).spliterator(), false).toList();
   }

    @Override
    public void deleteById({{keyType}} id) {
       requireNonNull(id, "id is required");
       this.template.delete(id);
    }

    public void deleteByIdIn(Iterable<{{keyType}}> ids) {
       requireNonNull(ids, "ids is required");
       this.template.delete(ids);
    }

    @Override
    public Optional<{{entityType}}> findById({{keyType}} id) {
        requireNonNull(id, "id is required");
        return this.template.get(id, {{entityType}}.class);
    }

    public Stream<{{entityType}}> findByIdIn(Iterable<{{keyType}}> ids) {
       requireNonNull(ids, "ids is required");
       return StreamSupport.stream(ids.spliterator(), false)
               .map(id -> this.template.get(id, {{entityType}}.class))
               .filter(Optional::isPresent)
               .flatMap(Optional::stream)
               .map({{entityType}}.class::cast)
               .filter(Objects::nonNull);
    }

    public boolean existsById({{keyType}} id) {
        requireNonNull(id, "id is required");
        return this.template.get(id, {{entityType}}.class).isPresent();
    }

    public long countBy() {
        throw new UnsupportedOperationException("Key-Value repository does not support count method");
    }

    public Page<{{entityType}}> findAll(PageRequest pageRequest, Order<{{entityType}}> order) {
        throw new UnsupportedOperationException("Key-Value repository does not support findAll method");
    }

    @Override
    public Stream<{{entityType}}> findAll() {
         throw new UnsupportedOperationException("Key-Value repository does not support findAll method");
    }

    {{#element.isNoSQLRepository}}
    @Override
    public void deleteAll() {
        throw new UnsupportedOperationException("Key-Value repository does not support deleteAll method");
    }
    {{/element.isNoSQLRepository}}

    @Override
    public void deleteAll(List<? extends {{entityType}}> entities) {
        Objects.requireNonNull(entities, "entities is required");
        entities.forEach(this::delete);
    }

    @Override
    public void delete({{entityType}} entity) {
        Objects.requireNonNull(entity, "entity is required");
        Object id = getIdField().read(entity);
        this.deleteById(({{keyType}}) id);
    }

    public <S extends {{entityType}}> S insert(S entity) {
        Objects.requireNonNull(entity, "entity is required");
        return this.template.put(entity);
    }

    public <S extends {{entityType}}> List<S> insertAll(List<S> entities) {
        Objects.requireNonNull(entities, "entities is required");
        return StreamSupport.stream(this.template.put(entities).spliterator(), false).toList();
    }

    public {{entityType}} update({{entityType}} entity) {
        Objects.requireNonNull(entity, "entity is required");
        return this.template.put(entity);
    }

    public <S extends {{entityType}}> List<S> updateAll(List<S> entities) {
        Objects.requireNonNull(entities, "entities is required");
        return StreamSupport.stream(this.template.put(entities).spliterator(), false).toList();
    }


    private FieldMetadata getIdField() {
        return entityMetadata().id().orElseThrow(KEY_NOT_FOUND_EXCEPTION_SUPPLIER);
    }

    {{#methods}}
    @Override
    public {{{returnType}}} {{methodName}}({{{parametersSignature}}}) {
    {{#parameters}}
       requireNonNull({{name}}, "{{name}} is required");
    {{/parameters}}
    {{#sourceCode}}
       {{{.}}};
    {{/sourceCode}}
    {{#hasReturn}}
       return {{{returnValue}}};
    {{/hasReturn}}
    }

    {{/methods}}


}
